#ifndef PPJTOOLS_H
#define PPJTOOLS_H

//#include <QtGui>
#include <QDebug>
//#include <QElapsedTimer>
#include <QFileInfo>
//#include <QDir>
#include <QApplication>
#include <QMessageBox>
#include <QDir>

#include <QString>
#include <QList>
#include <QDebug>
#include "config.h"

#define QT_USE_FAST_CONCATENATION
#define QT_USE_FAST_OPERATOR_PLUS


//--------------------------------------------------------------------------------------------------
/**
 * @brief The Table2D class
 */
class Table2D
{
public:

    inline explicit Table2D (const int tabSizeX = 64, const int tabSizeY = 64) :
        m_data (new int [tabSizeX * tabSizeY]),
        m_sizeX (abs (tabSizeX)),
        m_sizeY (abs (tabSizeY))
    {
        for (int i = 0 ; i < m_sizeX ; ++i) operator() (i,0) = i;
        for (int j = 1 ; j < m_sizeY ; ++j) operator() (0,j) = j;
    }

    inline ~Table2D () { delete m_data; }

    inline int &operator() (int x, int y) { // left operand
        return *(m_data + (x + (y * m_sizeX)));
    }

    inline int operator() (int x, int y) const { // right operand
        return *(m_data + (x + (y * m_sizeX)));
    }

    inline int sizeX () const { return m_sizeX; }
    inline int sizeY () const { return m_sizeY; }

private :
    int			*m_data;
    const int	m_sizeX;
    const int	m_sizeY;

    static inline int abs (int a) { return (a >= 0) ? a : -a; }
};

//--------------------------------------------------------------------------------------------------
/**
 * @brief The FileInfo class
 * simplifier la gestion des noms de fichiers
 * sans devoir rajouter de / ou de .
 * avec path (), name () et extension ()
 */
class FileInfo : public QFileInfo
{
public:
    FileInfo (const QString &fullPath = "/");

    QString path () const;
    bool setPath (const QString &p);

    QString name () const;
    bool setName (const QString &n);

    QString extension () const;
    bool setExtension (const QString &e);

    QString completeName () const;

    QString completeBaseName() const;
private:
    static const QString m_format;
};
/*----------------------------------------------------------------------------*/
class StringTool
{
private:

    Table2D m_distance;

public:

    enum  { DistanceErr=-1 };

    static inline int min (const int a, const int b) {
        if (a < b) return a; return b;
    }

    static inline int max (const int a, const int b) {
        if (a > b) return a; return b;
    }

    static inline int min (const int a, const int b, const int c) {
        return min (a, min (b, c));
    }

    static inline int abs (const int a) {
        if (a < 0) return -a; return a;
    }

    static inline int min (const int a, const int b, const int c, const int d) {
        return min (min (a, b), min (c, d));
    }

    static QString vowels () { return "AEIOUY"; }
    static QString soundexLettersToDelete () { return "AEHIOUWY"; }

    static bool isAVowel (const QChar& car) { return vowels ().contains (car); }
    static bool isAConsonant (const QChar& car) { return ! isAVowel (car); }

    static QString soundexCode (const QString &word);
    static char soundexInternalCode (const char& car);
    static QString removeFrom (const QString& word, const QString& letters);

    static QString uniqueLetters (const QString& word);
    static QString ordered (const QString& word);	// renvoie les lettres contenues dans le mot en ordre alphab
    static QString orderedUnique (const QString& word);

    inline int distance (const bool useDamerau, const QString &word1, const QString &word2, const int maxDistance)
    {
        if (useDamerau)
            return dlDistance (word1, word2, maxDistance);
        else
            return lDistance (word1, word2, maxDistance);
    }

    int dlDistance (const QString &word1, const QString &word2, const int maxDistance);
    int lDistance (const QString &word1, const QString &word2, const int maxDistance);
};

//------------------------------------------------------------
inline QString StringTool::uniqueLetters (const QString &word)
{
    QString ret;

    QString::const_iterator iter;

    for (iter = word.constBegin () ; iter != word.constEnd () ; iter++)
    {
        const QChar& car = *iter;
        if (! ret.contains (car))
        {
            ret += car;
        }
    }

    return ret;
}
//------------------------------------------------------
inline QString StringTool::ordered (const QString &word)
{

    QString result (word);
    qSort (result.begin (), result.end ());

    return result;
}
//------------------------------------------------------------
inline QString StringTool::orderedUnique (const QString &word)
{
    QString result;

    for (int car = 'A' ; car <= 'Z' ; ++car)
    {
        if (word.contains (car))
        {
            result += car;
        }
    }

    return result;
}


class FileInfo;


#if defined (TRACE_PPJ)
#define CHRONO_START {QElapsedTimer __chronoPPJ; __chronoPPJ.start();
#define CHRONO_END qDebug() << QString ("%1 exécuté en %2 ms").arg(Q_FUNC_INFO).arg(__chronoPPJ.elapsed());}
#else
#define CHRONO_START
#define CHRONO_END
#endif


inline static bool isWithin (int value, int min, int max)
{
    return value >= min and value <= max;
}

inline static int min (int a, int b)
{
    return (a < b) ? a : b;
}

inline static int max (int a, int b)
{
    return (a > b) ? a : b;
}

//inline static int abs (int a)
//{
//    return (a < 0) ? -a : a;
//}

inline static int sign (int a)
{
    return (a == 0) ? 0 : (a > 0) ? 1 : -1;
}

inline static QString toFirstCap (const QString &word)
{
    return (word.at (0).toUpper () + word.mid (1).toLower ());
}

inline static QString toAllCap (const QString &word)
{
    return (word.toUpper ());
}

inline static bool isFirstCap (const QString &word)
{
    return (word == toFirstCap (word));
}

inline static bool isAllCap (const QString &word)
{
    return (word == word.toUpper ());
}
//--------------------------------------------------------------------------------------------------
inline static bool userAgrees (QWidget *parent, const QString &question)
{
    int ret = QMessageBox::warning(parent, qApp->applicationName (), question, QMessageBox::Ok | QMessageBox::Cancel);
    return ret == QMessageBox::Ok;
}
//--------------------------------------------------------------------------------------------------
inline static bool isAllDigits (const QString &word)
{
    foreach (const QChar &car, word) if (not car.isDigit ()) return false;
    return true;
}
//--------------------------------------------------------------------------------------------------
inline static QChar unaccented (const QChar &car)
{
    const QString decomp (car.decomposition ());
    return (decomp.isEmpty ()) ? car : decomp.at (0);
}
//--------------------------------------------------------------------------------------------------
inline static QString unaccented (const QString &str)
{
    QString result;
    foreach (const QChar &car, str) {
        result += unaccented (car);
    }
    return result;
}
//------------------------------------------------------------------------
inline static QChar soundexCode (const QChar &car, const QString &lang)
{
    QChar candidate ('?');

    if (lang == "fr")
    {
        switch (car.toLatin1 ())
        {
        case 'B':
        case 'P':
            candidate = '1' ;
            break;

        case 'C':
        case 'K':
        case 'Q':
            candidate = '2' ;
            break;

        case 'D':
        case 'T':
            candidate = '3' ;
            break;

        case 'L':
            candidate = '4' ;
            break;

        case 'M':
        case 'N':
            candidate = '5' ;
            break;

        case 'R':
            candidate = '6' ;
            break;

        case 'G':
        case 'J':
            candidate = '7' ;
            break;

        case 'X':
        case 'Z':
        case 'S':
            candidate = '8' ;
            break;

        case 'F':
        case 'V':
            candidate = '9' ;
            break;
        }
    }
    else
    {
        // anglais
        switch (car.toLatin1 ())
        {
        case 'B':
        case 'P':
            candidate = '1' ;
            break;
        }
    }

    return candidate;
}

//--------------------------------------------------------------------------------------------------
inline static QString soundex (const QString &str, const QString &lang = "fr")
{
    static const QString allowed = QString ("bcdfgjklmnpqrstvxz").toUpper ();

    QString tmp = unaccented (str.toUpper ());
    QString result = tmp.left (1);
    tmp = tmp.mid (1);

    foreach (const QChar &car, tmp) {
        if (allowed.contains (car)) {
            QChar candidate = soundexCode (car, lang);
            if (!result.endsWith (candidate))
                result += candidate;
        }
    }

    result += "000";
    return result.left (4);
}

//--------------------------------------------------------------------------------------------------
inline static QString signature (const QString &word)
{
    static const QString consonants = "bcdfghjklmnpqrstvwxz";

    QString result;
    if (word.length () == 0) return result;

    const QString lower = word.toLower ();
    const QChar firstCar = lower.at (0);

    foreach (const QChar &car, consonants) {
        if (car != firstCar and lower.contains (car, Qt::CaseSensitive)) {
            result += car;
        }
    }

    result.prepend (unaccented (firstCar));
    return result;
}


//static void xCopy (const QString &sourcePath, const QString &destPath, const bool overwrite = true)
//{
//    static const QStringList filters = QStringList () << "*";
//    static const QDir::Filters flags = QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Files | QDir::Hidden;

////    QApplication::alert (this, 0);
//    FileInfo sourceInfo (sourcePath);
//    if (not sourceInfo.exists ()) return;
//    FileInfo destInfo (destPath);
//    if (destInfo.exists () and not overwrite) return;

//    if (sourceInfo.isDir ()) {
//        QString name = sourceInfo.completeBaseName ();
//        destInfo.setName (name);
//        QDir destDir (destInfo.completeName ());
//        destDir.mkdir (name);
//    }


//    //    QString sourceObjectPath = sourcePath + name;
//    //    QString destObjectPath = destPath + name;
//    //    QFileInfo fi (sourceObjectPath);

//    //    if (fi.isDir ()) {
//    //        qDebug () << "Créer le répertoire " << destObjectPath;
//    //        QDir destDir (destPath);
//    //        destDir.mkdir (name);

//    //        qDebug () << "Recopier dedans, récursivement, le contenu de" << sourceObjectPath;
//    //        sourceObjectPath += '/';
//    //        destObjectPath += '/';
//    //        QDir currentSourceDir (sourceObjectPath);
//    //        const QStringList fileList = currentSourceDir.entryList (filters, flags);
//    //        foreach (const QString &content, fileList) {
//    //            xCopy (sourceObjectPath, destObjectPath, content);
//    //        }
//    //    } else {
//    //        qDebug () << "Copier le fichier " << name;
//    //        QFile::copy (sourceObjectPath, destObjectPath);
//    //    }

//}


#endif // PPJTOOLS_H
